/**************************************************************************
* Copyright (c) 2015-2024 CrazyGeeker@163.com
*
* Author: CrazyGeeker@163.com  QQ: 453867443
*         https://gitee.com/crazy-geeker
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
*****************************************************************************
*/

#include "QTerminalWidget.h"
#include <QThread>
#include <QDebug>
#include <qapplication.h>
#include <QScreen>

QTerminalWidget::QTerminalWidget(const QString& command, const QString& currentDir, QWidget *parent) :
    QWidget(parent),
    m_command(command),
    m_hCmdWnd(0),
    m_hCmdProcess(NULL)
{
    if (start(command, currentDir)) {

        SetParent(m_hCmdWnd, (HWND)winId());
        ShowWindow(m_hCmdWnd, SW_SHOW);
        SetWindowLong(m_hCmdWnd, GWL_STYLE, WS_VISIBLE);
    }
}

void QTerminalWidget::resizeEvent(QResizeEvent *event)
{
    if (m_hCmdWnd != 0) {
        QRect rect = this->rect();
        
        auto screen = QApplication::screens()[0];
        screen->devicePixelRatio();

        MoveWindow(
            m_hCmdWnd, 
            rect.x(), rect.y(), 
            rect.width() * screen->devicePixelRatio(), 
            rect.height() * screen->devicePixelRatio(), 
            TRUE);
    }
}

struct EnumParams {
    DWORD dwThreadId;
    HWND hCmdWnd;
};

BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM param)
{
    EnumParams *params = (EnumParams*)param;

    DWORD processId = 0;

    if (GetWindowThreadProcessId(hWnd, &processId) == params->dwThreadId) {
        params->hCmdWnd = hWnd;
        return FALSE;
    }

    params->hCmdWnd = 0;

    return TRUE;
}

bool QTerminalWidget::start(const QString& command, const QString& currentDir)
{
    PROCESS_INFORMATION pi;

    STARTUPINFO si;
    si.cb = sizeof(STARTUPINFO);
    si.lpReserved = NULL;
    si.lpDesktop = NULL;
    si.lpTitle = NULL;
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
    si.cbReserved2 = NULL;
    si.lpReserved2 = NULL;

    if(CreateProcess(NULL, (LPWSTR)command.data(), NULL , NULL, FALSE, 0, NULL, (LPWSTR)(currentDir.isEmpty() ? NULL : currentDir.data()), &si, &pi)) {

        int retry = 500;

        do {

            QThread::msleep(10);

            EnumParams params;
            params.dwThreadId = pi.dwThreadId;
            params.hCmdWnd = 0;

            EnumWindows(EnumWindowsProc, (LPARAM)&params);

            if (params.hCmdWnd != 0) {
                m_hCmdWnd = params.hCmdWnd;
                m_hCmdProcess = pi.hProcess;
                return true;
            }

        } while (retry-- > 0);
    }
    else {
        qDebug() << "Create Process failed, error code " << GetLastError();
    }

    if (pi.hProcess != NULL)
        TerminateProcess(pi.hProcess, 0);

    return false;
}

QTerminalWidget::~QTerminalWidget()
{
    if (m_hCmdWnd != 0) {
        DestroyWindow(m_hCmdWnd);
        m_hCmdWnd = 0;
    }

    if (m_hCmdProcess != NULL) {
        TerminateProcess(m_hCmdProcess, 0);
        m_hCmdProcess = NULL;
    }
}
